import mc, string

try:
	import version
except:
	mc.LogDebug("SageBoxee: Exception accessing version.py.")
	mc.ShowDialogOk("Application can't be started.","The file 'version.py' is missing or corrupt.")
	exit(0)

'''A python wrapper to manage local application configuration.

This module provides a wrapper around local configuration operations.

In addition, the module defines a MediaPathMapper class that performs
conversions of media paths from the SageTV local version to a SMB path
visible to the Boxee device.
'''


'''Define a global media mapper object.

This object will be updated automatically when MediaPathMaps is changed, which
eliminates the overhead of creating the MediaPathMapper multiple times.
'''
mediaPathMapper = None


def initialize():
	'''Initializes the configuration wrapper.'''
	cfg_ver = _getValue('AppVersion')
	app_ver = version.ver
	
	# Perform required initialization
	refreshMediaPathMapper()
		
	# Store the current app version
	_setValue('AppVersion', app_ver)

def resetAll():
	'''Resets all values stored in configuration.'''
	mc.GetApp().GetLocalConfig().ResetAll()

def _getValue(k):
	'''Retrieves a value from the application configuration.

		     k: A string representing the configuration item key
		return: A string containing the configuration item value
	'''
	return mc.GetApp().GetLocalConfig().GetValue(k)
	
def _setValue(k, v):
	'''Stores a value into the application configuration.

		     k: A string representing the configuration item key
			 v: A string containing the configuration item value
		return: None
	'''
	mc.GetApp().GetLocalConfig().SetValue(k, v)
	
	
#
# Configuration Parameters
#

def getSageServer():
	'''Retrieves the SageTV server information.
			Format: "server:port"
	'''
	return _getValue('SageServer')
	
def setSageServer(value):
	'''Stores the SageTV server information.
			Format: "server:port"
	'''
	_setValue('SageServer', value)
	
	
def getJettyUser():
	'''Retrieves the username used to connect to the SageTV Jetty server.'''
	return _getValue('JettyUser')
	
def setJettyUser(value):
	'''Stores the username used to connect to the SageTV Jetty server.'''
	_setValue('JettyUser', value)
	
	
def getJettyPassword():
	'''Retrieves the password used to connect to the SageTV Jetty server.'''
	return _getValue('JettyPassword')
	
def setJettyPassword(value):
	'''Stores the password used to connect to the SageTV Jetty server.'''
	_setValue('JettyPassword', value)
	
	
def getMediaPathMaps():
	'''Retrieves a delimtied list of media path maps. 
			Format: "localPathA,remotePathA;localPathB,remotePathB;..."
	'''
	return _getValue('MediaPathMaps')
	
def setMediaPathMaps(value):
	'''Stores a delimtied list of media path maps. 
			Format: "localPathA,remotePathA;localPathB,remotePathB;..."
	'''
	_setValue('MediaPathMaps', value)
	refreshMediaPathMapper()


def getUseSSL():
	return _getValue('UseSSL')=='True'
	
def setUseSSL(value):
	_setValue('UseSSL', str(value))
	
def getRawUseSSL():
	return _getValue('UseSSL')
	
#
# Live TV
#

# Size that recording must grow to before live TV is started.
def getLiveTvBufferSize(d=2000000):
	sizeString = _getValue('LiveTvBuffer')
	if sizeString == '':
		setLiveTvBufferSize(d)
		return d
	else:
		return int(sizeString)
	
def setLiveTvBufferSize(value):
	_setValue('LiveTvBuffer', str(value))

# Maximum number of seconds that we will wait for Boxee to load the MediaFile.	
def getLiveTvWaitForLoadSecs(d=15):
	secs = _getValue('LiveTvWaitForload')
	if secs == '':
		setLiveTvWaitForLoadSecs(d)
		return d
	else:
		return int(secs)
	
def setLiveTvWaitForLoadSecs(value):
	_setValue('LiveTvWaitForload', str(value))

# Number of seconds to skip forward or backwards if << or >> is pressed.	
def getLiveTvBigSkip(d=150):
	secs = _getValue('LiveTvBigSkip')
	if secs == '':
		setLiveTvBigSkip(d)
		return d
	else:
		return int(secs)
	
def setLiveTvBigSkip(value):
	_setValue('LiveTvBigSkip', str(value))
	
# Number of seconds to skip forward or backwards if < or > is pressed.	
def getLiveTvSmallSkip(d=30):
	secs = _getValue('LiveTvSmallSkip')
	if secs == '':
		setLiveTvSmallSkip(d)
		return d
	else:
		return int(secs)
	
def setLiveTvSmallSkip(value):
	_setValue('LiveTvSmallSkip', str(value))
	
# The index into the channel list of the last channel that was watched live.	
def getLiveTvLastChanIndex(d=0):
	i = _getValue('LiveTvLastChanIndex')
	if i == '':
		setLiveTvLastChanIndex(d)
		return d
	else:
		return int(i)
	
def setLiveTvLastChanIndex(value):
	_setValue('LiveTvLastChanIndex', str(value))
	
# The OSD autohide time.	
def getOsdCloseTime(d=15):
	i = _getValue('OsdCloseTime')
	if i == '':
		setOsdCloseTime(d)
		return d
	else:
		return int(i)
	
def setOsdCloseTime(value):
	_setValue('OsdCloseTime', str(value))	
	
#
# MediaPathMapper Support
#
	
def refreshMediaPathMapper():
	'''Updates the global MediaPathMapper object to reflect changes in configuration.'''
	global mediaPathMapper
	mediaPathMapper = MediaPathMapper(getMediaPathMaps())
	
class MediaPathMapper:
	'''Converts media file paths from SageTV local path to network accessible path.'''
	PROP_PART_DELIM = ','
	PROP_MAP_DELIM  = ';'
	
	def __init__(self, init_str, network_prefix = r'smb://'):
		'''Constructor.

			      init_str: The initialization string containing a delimited list of media path maps.
			network_prefix: The prefix that will be appended to network paths.
		'''
		self._sorted_keys = []
		self._map_dict = {}
		self.network_prefix = network_prefix
		
		# Populate the map dictionary from the init string
		for mp in init_str.split(self.PROP_MAP_DELIM):
			map_parts = mp.split(self.PROP_PART_DELIM)
			if len(map_parts) == 2:
				self.addMap(map_parts[0], map_parts[1])
			
	def _updateKeys(self):
		'''Updates the sorted keys list so it stays in sync with the map dictionary. Keys are sorted in reverse order by length.'''
		del self._sorted_keys[:]
		self._sorted_keys = self._map_dict.keys()
		self._sorted_keys.sort(key = len)
		self._sorted_keys.reverse()
	
	def _preparePath(self, path, striplead):
		'''Prepares a path string for internal usage.

			     path: A string containing the path to prepare
			striplead: A boolean value indicating if leading slashes should be stripped
			   return: A string containing the converted path, or the original path if no conversion was necessary
		'''
		path = path.replace('\\', '/')
		path = path.strip()
		path = path.rstrip('/')
		if striplead:
			path = path.lstrip('/')
		return path
		
	def _convertPathInternal(self, map_key, path):
		'''Converts a path value using the specified map.

		map_key: A string indicating the map to use for the conversion
		   path: A string containing the path to convert
		 return: A string containing the converted path.
		'''
		origpath = path
		if map_key is None:
			path = path.lstrip('/')
		else:
			path = self._map_dict[map_key] + path[len(map_key):]
		path = self.network_prefix + path
		mc.LogDebug('MediaPathMapper: Converted path "%s" to "%s"' % (origpath, path))
		return path
		
	def addMap(self, local_prefix, remote_prefix):
		'''Adds a map entry into the dictionary.

			 local_prefix: A string containing the local path prefix (ie "C:\SageTV")
			remote_prefix: A string containing the remote path prefix (ie "//mediaserver/TV")
				   return: None
		'''
		# Prepare the prefix values
		local_prefix = self._preparePath(local_prefix, True)
		remote_prefix = self._preparePath(remote_prefix, True)
		
		# Add the value to the dictionary and update the sorted keys
		mc.LogDebug('MediaPathMapper: Adding map "%s" -> "%s"...' % (local_prefix, remote_prefix))
		self._map_dict[local_prefix] = remote_prefix
		self._updateKeys()
		
		
	def removeMap(self, local_prefix):
		'''Removes a map entry from the dictionary.
		
			local_prefix: A string containing the local path prefix (ie "C:\SageTV")
				  return: None
		'''
		mc.LogDebug('MediaPathMapper: Removing map "%s"...' % local_prefix)
		del self._map_dict[local_prefix]
		self._updateKeys()
		
	def clearMaps(self):
		'''Clears all entries from the map dictionary.'''
		mc.LogDebug('MediaPathMapper: Clearing maps...')
		self._map_dict.clear()
		self._updateKeys()
		
	def maps(self):
		'''Returns the list of map entries as key-value-pairs.'''
		return self._map_dict.items
		
	def implodeMaps(self):
		'''Retrieves a delimtied list of the map entries. 
				Format: "localPathA,remotePathA;localPathB,remotePathB;..."
		'''
		return self.PROP_MAP_DELIM.join(["%s%s%s" % (k, self.PROP_PART_DELIM, v) for k, v in self._map_dict.items()])
		
	def convertPath(self, path):
		'''Converts a media file path using the closest matching map entry.
		
			The conversion algorithm first performs a case-sensitive search for the closest matching map value. If
			that fails, a case-insensitive search is performed.  If both searches fail, the method determines if the
			path is a network path, and if so, appends the network prefix to the path before returning it.
		
			path: A string containing the media file path to convert
			return: A string containing the converted path, or the original path if no conversion was necessary
		'''
		origpath = path
		path = self._preparePath(path, False)
		
		# Search for case-sensitive match first
		for k in self._sorted_keys:
			if path.startswith(k):
				return self._convertPathInternal(k, path)
				
		# Search for case-insensitive match
		lower_path = path.lower()
		for k in self._sorted_keys:
			if lower_path.startswith(k.lower()):
				return self._convertPathInternal(k, path)
				
		# No match was found, check if the path is a network path
		if path.startswith('//'):
			return self._convertPathInternal(None, path)
			
		# Return the original path
		mc.LogDebug('MediaPathMapper: Cannot convert path "%s"' % path)
		return origpath
		
#
# Filtering. The filter is the string that will be used in the FilterByMethod() Sage API.  Typically this is one or more
# Sage methods separated by a '|'.  e.g. 'IsLibraryFile|IsWatched'.
#

# Get the include filter string.
def getRecordingsIncludeFilter():
	return _getValue('RecIncludeFilter')
	
# Get the exclude filter string.
def getRecordingsExcludeFilter():
	return _getValue('RecExcludeFilter')

# Check if the filter string includes 'value'.	
def doesIncludeFilterContain(value):
	filter = _getValue('RecIncludeFilter')
	if filter == '' or value == '':
		print "SageBoxee: doesIncludeFilterContain filter or value is null."
		return False
	else:
		return filter.find(value) != -1
	
# Add 'value' to the filter string.	
def addToIncludeFilter(value):
	print "SageBoxee: Include filter adding " + value
	if value == '' or doesIncludeFilterContain(value):
		return
	else:
		filter = _getValue('RecIncludeFilter')	
		if filter != '':
			filter += '|'
		filter += value
		_setValue('RecIncludeFilter', filter)
		print "SageBoxee: Include filter after adding " + filter
		
# Remove 'value' from the filter string.	
def removeFromIncludeFilter(value):
	print "SageBoxee: Include filter removing " + value
	if value == '' or (not doesIncludeFilterContain(value)):
		print "SageBoxee: Nothing to remove."
		return
	else:
		filter = _getValue('RecIncludeFilter')
		filter = string.replace(filter,value,'')
		filter = string.replace(filter,'||','|')
		if filter == '|':
			filter = ''
		else:
			filter = string.lstrip(filter, '|')
			filter = string.rstrip(filter, '|')			
		_setValue('RecIncludeFilter', filter)
		print "SageBoxee: Include filter after removing " + filter
	
def getRecordingsExcludeFilter():
	return _getValue('RecExcludeFilter')
	
def setRecordingsExcludeFilter(value):
	_setValue('RecExcludeFilter', value)
	
def doesExcludeFilterContain(value):
	filter = _getValue('RecExcludeFilter')
	if filter == '' or value == '':
		print "SageBoxee: doesExcludeFilterContain filter or value is null."
		return False
	else:
		return filter.find(value) != -1
		
def addToExcludeFilter(value):
	print "SageBoxee: Exclude filter adding " + value
	if value == '' or doesExcludeFilterContain(value):
		return
	else:
		filter = _getValue('RecExcludeFilter')
		if filter != '':
			filter += '|'
		filter += value
		_setValue('RecExcludeFilter', filter)
		print "SageBoxee: Exclude filter after adding " + filter
		
def removeFromExcludeFilter(value):
	print "SageBoxee: Exclude filter removing " + value
	if value == '' or (not doesExcludeFilterContain(value)):
		print "SageBoxee: Nothing to remove."
		return
	else:
		filter = _getValue('RecExcludeFilter')
		filter = string.replace(filter,value,'')
		filter = string.replace(filter,'||','|')
		if filter == '|':
			filter = ''
		else:
			filter = string.lstrip(filter, '|')
			filter = string.rstrip(filter, '|')
		_setValue('RecExcludeFilter', filter)
		print "SageBoxee: Exclude filter after removing " + filter
	
def getEpisodeSortStyle(d="Title"):
	val = _getValue('EpisodeSortStyle')
	if val == '':
		setEpisodeSortStyle(d)
		return d
	else:
		return val
	
def setEpisodeSortStyle(value):
	_setValue('EpisodeSortStyle', str(value))